package jsen.tool.ddd.adapter.infrastructure.specification.base;

import jsen.tool.ddd.adapter.infrastructure.specification.ISpecification;
import jsen.tool.ddd.adapter.infrastructure.specification.Specification;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @author jsen
 * @date 2021/10/11
 */
@Specification
public abstract class CompositeSpecification<T> implements ISpecification<T> {

    private final Map<CompositeSpecification<?>, Function<T, ?>> specificationMap = new HashMap<>(4);

    public <S> void addSpecification(Function<T, S> changeContext, CompositeSpecification<S> specification) {
        specificationMap.put(specification, changeContext);
    }

    /**
     * @return 所有支持的specItem，null 代表任何Spec都支持，空list代表任何Spec都不支持
     */
    public SpecConfig getSpecConfig() {
        return SpecConfig.createAny();
    }

    Map<CompositeSpecification<?>, Function<T, ?>> getSpecificationMap(SpecContext<T> t) {
        Map<CompositeSpecification<?>, Function<T, ?>> result = new HashMap<>(specificationMap.size());
        specificationMap.forEach((k, v) -> {
            if (t.isNeedSpec(k)) {
                result.put(k, v);
            }
        });
        return result;
    }

    public CompositeSpecification<T> and(ISpecification<T> specification) {
        CompositeSpecification<T> target = getCompositeSpecification(specification);
        if (this instanceof And) {
            this.addSpecification(Function.identity(), target);
            return this;
        } else {
            And<T> and = new And<>();
            and.addSpecification(Function.identity(), this);
            and.addSpecification(Function.identity(), target);
            return and;
        }
    }

    public <S> CompositeSpecification<T> and(Function<T, S> changeContext, ISpecification<S> specification) {
        CompositeSpecification<S> target = getCompositeSpecification(specification);
        if (this instanceof And) {
            this.addSpecification(changeContext, target);
            return this;
        } else {
            And<T> and = new And<>();
            and.addSpecification(Function.identity(), this);
            and.addSpecification(changeContext, target);
            return and;
        }
    }


    public CompositeSpecification<T> or(ISpecification<T> specification) {
        CompositeSpecification<T> target = getCompositeSpecification(specification);
        if (this instanceof Or) {
            this.addSpecification(Function.identity(), target);
            return this;
        } else {
            Or<T> or = new Or<>();
            or.addSpecification(Function.identity(), this);
            or.addSpecification(Function.identity(), target);
            return or;
        }
    }

    public <S> CompositeSpecification<T> or(Function<T, S> changeContext, ISpecification<S> specification) {
        CompositeSpecification<S> target = getCompositeSpecification(specification);
        if (this instanceof Or) {
            this.addSpecification(changeContext, target);
            return this;
        } else {
            Or<T> or = new Or<>();
            or.addSpecification(Function.identity(), this);
            or.addSpecification(changeContext, target);
            return or;
        }
    }

    private <A> CompositeSpecification<A> getCompositeSpecification(ISpecification<A> specification) {
        CompositeSpecification<A> target;
        if (!(specification instanceof CompositeSpecification)) {
            target = new FilterSpecification<>(specification);
        } else {
            target = ((CompositeSpecification<A>) specification);
        }
        return target;
    }
}
